// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <arith_uint256.h>

#include <uint256.h>
#include <utilstrencodings.h>
#include <crypto/common.h>

#include <stdio.h>
#include <string.h>

template <unsigned int BITS>
base_uint<BITS>::base_uint(const std::string &str) {
  static_assert(
      BITS / 32 > 0 && BITS % 32 == 0,
      "Template parameter BITS must be a positive multiple of 32.");

  SetHex(str);
}

template <unsigned int BITS>
base_uint<BITS> &base_uint<BITS>::operator<<=(unsigned int shift) {
  base_uint<BITS> a(*this);
  for (int i = 0; i < WIDTH; i++)
    pn[i] = 0;
  int k = shift / 32;
  shift = shift % 32;
  for (int i = 0; i < WIDTH; i++) {
    if (i + k + 1 < WIDTH && shift != 0)
      pn[i + k + 1] |= (a.pn[i] >> (32 - shift));
    if (i + k < WIDTH)
      pn[i + k] |= (a.pn[i] << shift);
  }
  return *this;
}

template <unsigned int BITS>
base_uint<BITS> &base_uint<BITS>::operator>>=(unsigned int shift) {
  base_uint<BITS> a(*this);
  for (int i = 0; i < WIDTH; i++)
    pn[i] = 0;
  int k = shift / 32;
  shift = shift % 32;
  for (int i = 0; i < WIDTH; i++) {
    if (i - k - 1 >= 0 && shift != 0)
      pn[i - k - 1] |= (a.pn[i] << (32 - shift));
    if (i - k >= 0)
      pn[i - k] |= (a.pn[i] >> shift);
  }
  return *this;
}

template <unsigned int BITS>
base_uint<BITS> &base_uint<BITS>::operator*=(uint32_t b32) {
  uint64_t carry = 0;
  for (int i = 0; i < WIDTH; i++) {
    uint64_t n = carry + (uint64_t)b32 * pn[i];
    pn[i] = n & 0xffffffff;
    carry = n >> 32;
  }
  return *this;
}

template <unsigned int BITS>
base_uint<BITS> &base_uint<BITS>::operator*=(const base_uint &b) {
  base_uint<BITS> a = *this;
  *this = 0;
  for (int j = 0; j < WIDTH; j++) {
    uint64_t carry = 0;
    for (int i = 0; i + j < WIDTH; i++) {
      uint64_t n = carry + pn[i + j] + (uint64_t)a.pn[j] * b.pn[i];
      pn[i + j] = n & 0xffffffff;
      carry = n >> 32;
    }
  }
  return *this;
}

template <unsigned int BITS>
base_uint<BITS> &base_uint<BITS>::operator/=(const base_uint &b) {
  base_uint<BITS> div = b; // make a copy, so we can shift.
  base_uint<BITS> num = *this; // make a copy, so we can subtract.
  *this = 0; // the quotient.
  int num_bits = num.bits();
  int div_bits = div.bits();
  if (div_bits == 0)
    throw uint_error("Division by zero");
  if (div_bits > num_bits) // the result is certainly 0.
    return *this;
  int shift = num_bits - div_bits;
  div <<= shift; // shift so that div and num align.
  while (shift >= 0) {
    if (num >= div) {
      num -= div;
      pn[shift / 32] |= (1 << (shift & 31)); // set a bit of the result.
    }
    div >>= 1; // shift back.
    shift--;
  }
  // num now contains the remainder of the division.
  return *this;
}

template <unsigned int BITS>
int base_uint<BITS>::CompareTo(const base_uint<BITS> &b) const {
  for (int i = WIDTH - 1; i >= 0; i--) {
    if (pn[i] < b.pn[i])
      return -1;
    if (pn[i] > b.pn[i])
      return 1;
  }
  return 0;
}

template <unsigned int BITS>
bool base_uint<BITS>::EqualTo(uint64_t b) const {
  for (int i = WIDTH - 1; i >= 2; i--) {
    if (pn[i])
      return false;
  }
  if (pn[1] != (b >> 32))
    return false;
  if (pn[0] != (b & 0xfffffffful))
    return false;
  return true;
}

template <unsigned int BITS>
double base_uint<BITS>::getdouble() const {
  double ret = 0.0;
  double fact = 1.0;
  for (int i = 0; i < WIDTH; i++) {
    ret += fact * pn[i];
    fact *= 4294967296.0;
  }
  return ret;
}

template <unsigned int BITS>
std::string base_uint<BITS>::GetHex() const {
  return ArithToUint256(*this).GetHex();
}

template <unsigned int BITS>
void base_uint<BITS>::SetHex(const char *psz) {
  *this = UintToArith256(uint256S(psz));
}

template <unsigned int BITS>
void base_uint<BITS>::SetHex(const std::string &str) {
  SetHex(str.c_str());
}

template <unsigned int BITS>
std::string base_uint<BITS>::ToString() const {
  return (GetHex());
}

template <unsigned int BITS>
unsigned int base_uint<BITS>::bits() const {
  for (int pos = WIDTH - 1; pos >= 0; pos--) {
    if (pn[pos]) {
      for (int nbits = 31; nbits > 0; nbits--) {
        if (pn[pos] & 1 << nbits)
          return 32 * pos + nbits + 1;
      }
      return 32 * pos + 1;
    }
  }
  return 0;
}

// Explicit instantiations for base_uint<256>
template base_uint<256>::base_uint(const std::string &);
template base_uint<256> &base_uint<256>::operator<<=(unsigned int);
template base_uint<256> &base_uint<256>::operator>>=(unsigned int);
template base_uint<256> &base_uint<256>::operator*=(uint32_t b32);
template base_uint<256> &base_uint<256>::operator*=(const base_uint<256> &b);
template base_uint<256> &base_uint<256>::operator/=(const base_uint<256> &b);
template int base_uint<256>::CompareTo(const base_uint<256> &) const;
template bool base_uint<256>::EqualTo(uint64_t) const;
template double base_uint<256>::getdouble() const;
template std::string base_uint<256>::GetHex() const;
template std::string base_uint<256>::ToString() const;
template void base_uint<256>::SetHex(const char *);
template void base_uint<256>::SetHex(const std::string &);
template unsigned int base_uint<256>::bits() const;

// This implementation directly uses shifts instead of going
// through an intermediate MPI representation.
arith_uint256 &arith_uint256::SetCompact(
    uint32_t nCompact, bool *pfNegative, bool *pfOverflow) {
  int nSize = nCompact >> 24;
  uint32_t nWord = nCompact & 0x007fffff;
  if (nSize <= 3) {
    nWord >>= 8 * (3 - nSize);
    *this = nWord;
  } else {
    *this = nWord;
    *this <<= 8 * (nSize - 3);
  }
  if (pfNegative)
    *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0;
  if (pfOverflow)
    *pfOverflow = nWord != 0 &&
        ((nSize > 34) || (nWord > 0xff && nSize > 33) ||
         (nWord > 0xffff && nSize > 32));
  return *this;
}

uint32_t arith_uint256::GetCompact(bool fNegative) const {
  int nSize = (bits() + 7) / 8;
  uint32_t nCompact = 0;
  if (nSize <= 3) {
    nCompact = GetLow64() << 8 * (3 - nSize);
  } else {
    arith_uint256 bn = *this >> 8 * (nSize - 3);
    nCompact = bn.GetLow64();
  }
  // The 0x00800000 bit denotes the sign.
  // Thus, if it is already set, divide the mantissa by 256 and increase the
  // exponent.
  if (nCompact & 0x00800000) {
    nCompact >>= 8;
    nSize++;
  }
  assert((nCompact & ~0x007fffff) == 0);
  assert(nSize < 256);
  nCompact |= nSize << 24;
  nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0);
  return nCompact;
}

uint256 ArithToUint256(const arith_uint256 &a) {
  uint256 b;
  for (int x = 0; x < a.WIDTH; ++x)
    WriteLE32(b.begin() + x * 4, a.pn[x]);
  return b;
}
arith_uint256 UintToArith256(const uint256 &a) {
  arith_uint256 b;
  for (int x = 0; x < b.WIDTH; ++x)
    b.pn[x] = ReadLE32(a.begin() + x * 4);
  return b;
}
